package uk.ac.ebi.bioinvindex.persistence;

import uk.ac.ebi.bioinvindex.model.impl.MaterialImpl;
import uk.ac.ebi.bioinvindex.model.impl.xref.ReferenceSourceImpl;
import uk.ac.ebi.bioinvindex.model.impl.term.*;
import uk.ac.ebi.bioinvindex.dao.ejb3.DaoFactory;
import uk.ac.ebi.bioinvindex.dao.OntologyEntryDAO;
import uk.ac.ebi.bioinvindex.dao.ReferenceSourceDAO;
import uk.ac.ebi.bioinvindex.utils.AccessionGenerator;

import java.util.Collection;

/**
 * User: Nataliya Sklyar (nsklyar@ebi.ac.uk)
 * Date: Jan 11, 2008
 */
public class PersisterHelper {

	protected DaoFactory daoFactory;

	public void prepareMaterial(MaterialImpl material) {

		MaterialRoleImpl role = material.getType();
		material.setType(persistOntologyEntry(MaterialRoleImpl.class, role));

		Collection<CharacteristicValueImpl> characteristicValues = material.getCharacteristicValues();
		for (CharacteristicValueImpl characteristicValue : characteristicValues) {

			prepareFreeTextTerm(characteristicValue);

			CharacteristicImpl characteristic = characteristicValue.getType();
			prepareFreeTextTerm(characteristic);

			UnitValue unit = characteristicValue.getUnit();
			if (unit != null) {
				prepareFreeTextTerm(unit);
				prepareFreeTextTerm(unit.getType());
			}
		}
	}

	public FreeTextTermImpl prepareFreeTextTerm(FreeTextTermImpl freeTextTerm) {

//		if (freeTextTerm.getId() != null) {
//			//ToDo: add support for update case: "new OntologyTerm was added to existing FTT"
//			if (!daoFactory.getEntityManager().contains(freeTextTerm)) {
//				return daoFactory.getEntityManager().merge(freeTextTerm);
//			} else {
//				return freeTextTerm;
//			}
//		}

		Collection<OntologyTermImpl> ontologyTerms = freeTextTerm.getOntologyTerms();
		for (OntologyTermImpl term : ontologyTerms) {
			OntologyTermImpl termDB = persistOntologyEntry(OntologyTermImpl.class, term);

			freeTextTerm.removeOntologyTerm(term);
			freeTextTerm.addOntologyTerm(termDB);
		}

		//ToDo: persist Annotations

		return freeTextTerm;
	}

	/**
	 * It is assumed that term has an accession, if accession is null, then BIIPersistenceException is thrown.
	 *
	 * @param term
	 * @return
	 */
//	public OntologyTermImpl persistOntologyTerm(OntologyTermImpl term) {
//
//		if (term.getAcc() == null) {
//			throw new BIIPersistenceException("OntologyTerm accession cannot be null! " + term);
//		}
//
//		ReferenceSourceImpl refSrcDB = persistReferenceSource(term.getSource());
//		term.setSource(refSrcDB);
//
//		OntologyEntryDAO<OntologyTermImpl> entryDAO = daoFactory.getOntologyEntryDAO(OntologyTermImpl.class);
//		OntologyTermImpl termDB = entryDAO.getOntologyEntryByAccAndRefSource(term.getAcc(), term.getSource().getAcc());
//		if (termDB == null) {
//			//Add OntologyTerm to DB
//			entryDAO.save(term);
//			return term;
//		} else {
//			return termDB;
//		}
//	}

	/**
	 * Persists ReferenceSource object. Creates a new record in the DB in a case source with a given name
	 * in not in the DB;
	 * if source name in the DB already, returns retreived object
	 *
	 * @param source
	 * @return
	 */
	public ReferenceSourceImpl persistReferenceSource(ReferenceSourceImpl source) {

		//ToDo: check if ID is not null, merge in this case

		ReferenceSourceDAO referenceSourceDAO = daoFactory.getReferenceSourceDAO();
		ReferenceSourceImpl sourceDB = referenceSourceDAO.getReferenceSourceByName(source.getName());

		if (sourceDB != null) {
			return sourceDB;

		} else {
			//RefSrc is not found in the DB - Create Acc and add to the DB
			if (source.getAcc() == null) {
				String tempAcc = AccessionGenerator.getInstance().generateTempAcc();
				source.setAcc(tempAcc);
				//ToDo: add Annotation
			} else {
				//ToDo: discuss, shall we save ref in this case?
				throw new BIIPersistenceException("Unknown ReferenceSource accession " + source.getAcc());
			}
			referenceSourceDAO.save(source);
			return source;
		}
	}

//	public MaterialRoleImpl persistType(MaterialRoleImpl type) {
//		if (type.getAcc() == null) {
//			throw new BIIPersistenceException("MaterialRole accession cannot be null!");
//		} else {
//			OntologyEntryDAO<MaterialRoleImpl> entryDAO = daoFactory.getOntologyEntryDAO(MaterialRoleImpl.class);
//			return entryDAO.getOntologyEntryByAccAndRefSource(type.getAcc(), type.getSource().getName());
//		}
//	}

	/**
	 * It is assumed that OntologyTetm object  has an accession, if accession is null, then BIIPersistenceException is thrown.
	 * For OntologyEntries which are BII CV terms, accession might be null, in this case temp acc is generated
	 * and corresponding Annotation is added.
	 * @param oeClass
	 * @param entry
	 * @return
	 */
	public <T extends OntologyEntryImpl> T persistOntologyEntry(Class<T> oeClass, T entry) {
		if (entry.getAcc() == null) {
			if (OntologyTermImpl.class.equals(oeClass)) {
				throw new BIIPersistenceException("OntologyEntry accession must be not null for persistence!");
			} else {
				String tempAcc = AccessionGenerator.getInstance().generateTempAcc();
				entry.setAcc(tempAcc);
				//ToDo: add Annotation
			}
		}

		ReferenceSourceImpl refSrcDB = persistReferenceSource(entry.getSource());
		entry.setSource(refSrcDB);

		OntologyEntryDAO<T> entryDAO = daoFactory.getOntologyEntryDAO(oeClass);
		T termDB = entryDAO.getOntologyEntryByAccAndRefSource(entry.getAcc(), entry.getSource().getAcc());

		if (termDB == null) {
			entryDAO.save(entry);
			return entry;
		} else {
			return termDB;
		}

	}

	public DaoFactory getDaoFactory() {
		return daoFactory;
	}

	public void setDaoFactory(DaoFactory daoFactory) {
		this.daoFactory = daoFactory;
	}
}
